﻿//-----------------------------------------------------------------------
// <copyright file="HtmlProcessor.cs" company="Redmist">
//     Copyright (c) Damien T Smith. All rights reserved.
// </copyright>
// <author>Damien Smith</author>
//-----------------------------------------------------------------------

namespace Redmist.UsageMonitor.UsageData
{
    using System;
    using System.Collections.Generic;
    using System.Text.RegularExpressions;

    /// <summary>Processes a HTML monthly usage page and extracts the usage data.</summary>
    public static class HtmlProcessor
    {
        /// <summary>Processes a HTML monthly usage page and extracts the usage data.</summary>
        /// <param name="htmlUsagePage">A System.String representing a HTML monthly usage web page.</param>
        /// <returns>A Redmist.UsageMonitor.UsageData.MonthlyUsage representing basic monthly usage statistical data.</returns>
        public static MonthlyUsage RetrieveMonthlyUsage(string htmlUsagePage)
        {
            IList<string> usageDataCells = ExtractUsageDataCells(htmlUsagePage);
            int startCellIndex = RetrieveDataStartIndex(usageDataCells);
            int quota = int.Parse(usageDataCells[startCellIndex + 17]);
            string planName = usageDataCells[startCellIndex + 20].Replace(" Data Charge", string.Empty);
            string excessRate = usageDataCells[startCellIndex + 21].Replace("Gb", "GB");
            string excessCharges = usageDataCells[startCellIndex + 23];

            DateTime usageLastUpdated = DateTime.Parse(usageDataCells[startCellIndex + 11]);
            string anniversary = usageDataCells[startCellIndex + 15];
            int anniversaryDay = int.Parse(anniversary.Substring(0, anniversary.Length - 16));
            AnniversaryData timingData = CalculateAnniversaryData(usageLastUpdated, anniversaryDay);

            UsageStatistics downloadUsage = RetrieveBasicUsage(usageDataCells, startCellIndex, 1, 5);
            UsageStatistics uploadUsage = RetrieveBasicUsage(usageDataCells, startCellIndex, 3, 7);
            UsageStatistics countedUsage;

            if (downloadUsage.Peak + downloadUsage.CountedOffPeak > uploadUsage.Peak + uploadUsage.CountedOffPeak)
            {
                countedUsage = downloadUsage;
            }
            else
            {
                countedUsage = uploadUsage;
            }

            int[] peakUploads = ExtractGraphData(htmlUsagePage, "&r1=Uploads;", "&r2=Downloads;");
            int[] peakDownloads = ExtractGraphData(htmlUsagePage, "&r2=Downloads;", "&r3=OffPeak Uploads;");
            int[] offPeakUploads = ExtractGraphData(htmlUsagePage, "&r3=OffPeak Uploads;", "&r4=OffPeak Downloads;");
            int[] offPeakDownloads = ExtractGraphData(htmlUsagePage, "&r4=OffPeak Downloads;", "&chart_type=");

            return new MonthlyUsage(downloadUsage, uploadUsage, countedUsage, timingData, quota, peakDownloads, offPeakDownloads, peakUploads, offPeakUploads, planName, excessRate, excessCharges);
        }

        /// <summary>Determines the starting index for usage data by locating the cell containing "Peak Downloads".</summary>
        /// <param name="cells">A List of HTML tables cells as strings representing usage data page table cells.</param>
        /// <returns>A value indicating the index of the cell containing "Peak Downloads".</returns>
        public static int RetrieveDataStartIndex(IList<string> cells)
        {
            int startingCell = 0;
            foreach (string item in cells)
            {
                if (item.Contains("Peak Downloads"))
                {
                    startingCell = cells.IndexOf(item);
                    break;
                }
            }

            return startingCell;
        }

        /// <summary>Calculates the anniversary dates used for various other calculations.</summary>
        /// <param name="usageLastUpdated">A System.DateTime representing the time on which to base the calculations.</param>
        /// <param name="anniversaryDay">The anniversary day of the month from which to generate the anniversary dates.</param>
        /// <returns>A Redmist.UsageMonitor.UsageData.AnniversaryData representing the anniversary data.</returns>
        public static AnniversaryData CalculateAnniversaryData(DateTime usageLastUpdated, int anniversaryDay)
        {
            DateTime lastAnniversary;
            DateTime nextAnniversary;
            if (usageLastUpdated.Day >= anniversaryDay)
            {
                lastAnniversary = new DateTime(usageLastUpdated.Year, usageLastUpdated.Month, anniversaryDay);
                nextAnniversary = lastAnniversary.AddMonths(1);
            }
            else
            {
                DateTime previousMonth = usageLastUpdated.AddMonths(-1);
                int lastAnniversaryMonthDayCount = DateTime.DaysInMonth(previousMonth.Year, previousMonth.Month);
                int nextAnniversaryMonthDayCount = DateTime.DaysInMonth(usageLastUpdated.Year, usageLastUpdated.Month);
                if (lastAnniversaryMonthDayCount > anniversaryDay)
                {
                    lastAnniversaryMonthDayCount = anniversaryDay;
                }

                if (nextAnniversaryMonthDayCount > anniversaryDay)
                {
                    nextAnniversaryMonthDayCount = anniversaryDay;
                }

                lastAnniversary = new DateTime(previousMonth.Year, previousMonth.Month, lastAnniversaryMonthDayCount);
                nextAnniversary = new DateTime(usageLastUpdated.Year, usageLastUpdated.Month, nextAnniversaryMonthDayCount);
            }

            return new AnniversaryData(usageLastUpdated, anniversaryDay, nextAnniversary, lastAnniversary, DateTime.Now);
        }

        /// <summary>Extracts all of the table cell values from a HTML page.</summary>
        /// <param name="htmlUsagePage">A System.String representing the HTML page from which to extract the cells.</param>
        /// <returns>A System.Collections.Generic.IList of System.String representing the usage page cells.</returns>
        private static IList<string> ExtractUsageDataCells(string htmlUsagePage)
        {
            Regex regex = new Regex("<td[^>]*>(.*?)</td>", RegexOptions.IgnoreCase);
            Match match = regex.Match(htmlUsagePage);
            List<string> tableCells = new List<string>();

            while (match.Success)
            {
                match = match.NextMatch();
                string cellValue = Regex.Replace(match.Value, "<(.|\n)*?>", string.Empty);
                cellValue = cellValue.Replace(" MBs", string.Empty);
                tableCells.Add(cellValue);
            }

            return tableCells;
        }

        /// <summary>Retrieves a single peak and off peak usage data element from the supplied table cell list.</summary>
        /// <param name="cells">A System.Collections.Generic.IList of System.String representing the usage page cells.</param>
        /// <param name="startIndex">The starting index from which to locate the usage cells.</param>
        /// <param name="peakCellIndex">The index to locate the peak usage value.</param>
        /// <param name="offPeakCellIndex">The index to locate the off peak usage value.</param>
        /// <returns>A Redmist.UsageMonitor.UsageData.UsageStatistics representing basic usage data.</returns>
        private static UsageStatistics RetrieveBasicUsage(IList<string> cells, int startIndex, int peakCellIndex, int offPeakCellIndex)
        {
            int peakUploads = int.Parse(cells[startIndex + peakCellIndex]);
            int countedOffPeakUploads = int.Parse(cells[startIndex + offPeakCellIndex]);
            return new UsageStatistics(peakUploads, countedOffPeakUploads);
        }

        /// <summary>Extracts a section of graph data from a usage statistics page and converts it to an array of integers.</summary>
        /// <param name="htmlPage">A System.String representing the HTML page from which to extract the graph data.</param>
        /// <param name="startSearchString">A System.String representing the starting string which encloses the graph data.</param>
        /// <param name="endSearchString">A System.String representing the ending string which encloses the graph data.</param>
        /// <returns>An array of integers representing a graph source data set.</returns>
        private static int[] ExtractGraphData(string htmlPage, string startSearchString, string endSearchString)
        {
            int startIndex = htmlPage.IndexOf(startSearchString) + startSearchString.Length;
            int endIndex = htmlPage.IndexOf(endSearchString);
            string[] values = htmlPage.Substring(startIndex, endIndex - startIndex).Split(';');
            return Array.ConvertAll<string, int>(values, int.Parse);
        }
    }
}
